package com.meme.io.packager;

import com.meme.editor.model.sprite2d.Sprite2dFile;
import java.io.RandomAccessFile;

import org.apache.log4j.Logger;

import com.meme.util.FileUtil;

import com.meme.editor.EntityObject;
import com.meme.editor.model.sprite2d.*;

public class Sprite2dBinFileWriter 
{
	static Logger logger = Logger.getLogger(Sprite2dBinFileWriter.class);
	
	public static void writeSprite2dBinFile (Sprite2dFile sprite2dFile, String filename) throws Exception
	{
		new Sprite2dBinFileWriter ().writeToFile(sprite2dFile, filename);
	}

	public void writeToFile (Sprite2dFile sprite2dFile, String filename)
	{	
		try
		{
			FileUtil.deleteFile (filename);
			
			RandomAccessFile binFile = new RandomAccessFile (filename, "rw");
			
			writeSprite2dFile (sprite2dFile, binFile);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	void writeSprite2dFile (Sprite2dFile sprite2dFile, RandomAccessFile binFile) throws Exception
	{
		// module images
		{
			Sprite2dFile.ModuleImageSet moduleImageSet = sprite2dFile.getModuleImageSet();		
			binFile.writeShort(moduleImageSet.getChildrenCount());
			
			for (EntityObject entity : moduleImageSet.getChildren())
			{
				ModuleImage moudleImage = (ModuleImage) entity;
				String imgUrl = moudleImage.getImageFilename();
				if (imgUrl != null)
					imgUrl = FileUtil.getRelativePathFilename(imgUrl, sprite2dFile.getHostFilename());
				else
					imgUrl = "";
				binFile.writeUTF (imgUrl);
			}
		}
		
		// pure module groups
		{
			Sprite2dFile.PureModuleGroupSet pureModuleGroupSet = sprite2dFile.getPureModuleGroupSet();
			binFile.writeShort (pureModuleGroupSet.getChildrenCount());
			
			for (EntityObject entity : pureModuleGroupSet.getChildren())
			{
				PureModuleGroup pureModuleGroup = (PureModuleGroup) entity;
				ModuleImage moduleImage = pureModuleGroup.getModuleImage(); 
				binFile.writeShort (moduleImage == null ? -1: moduleImage.getIndex());
				binFile.writeShort (pureModuleGroup.getChildrenCount());
				
				for (EntityObject pureModuleEntity : pureModuleGroup.getChildren())
				{
					PureModule pureModule = (PureModule) pureModuleEntity;
					binFile.writeShort (pureModule.getX());
					binFile.writeShort (pureModule.getY());
					binFile.writeShort (pureModule.getWidth());
					binFile.writeShort (pureModule.getHeight());
				}
			}
		}
		
		// composited module group
		{
			Sprite2dFile.CompositedModuleGroupSet compositedModuleGroupSet = sprite2dFile.getCompositedModuleGroupSet();
			binFile.writeShort (compositedModuleGroupSet.getChildrenCount());
			
			for (EntityObject entity : compositedModuleGroupSet.getChildren())
			{
				CompositedModuleGroup compositedModuleGroup = (CompositedModuleGroup) entity;
				binFile.writeShort (compositedModuleGroup.getChildrenCount());
				
				for (EntityObject compositedModuleEntity : compositedModuleGroup.getChildren())
				{
					CompositedModule compositedModule = (CompositedModule) compositedModuleEntity;
					binFile.writeShort(compositedModule.getChildrenCount());
					
					for (EntityObject modulePartEntity : compositedModule.getChildren())
					{
						ModulePart modulePart = (ModulePart) modulePartEntity;
						writeModulePart (modulePart, binFile);
					}
				}
			}
		}
		
		// animated module group
		{
			Sprite2dFile.AnimatedModuleGroupSet animatedModuleGroupSet = sprite2dFile.getAnimatedModuleGroupSet();
			binFile.writeShort (animatedModuleGroupSet.getChildrenCount());
			
			for (EntityObject entity : animatedModuleGroupSet.getChildren())
			{
				AnimatedModuleGroup animatedModuleGroup = (AnimatedModuleGroup) entity;
				binFile.writeShort (animatedModuleGroup.getChildrenCount());
				
				for (EntityObject animatedModuleEntity : animatedModuleGroup.getChildren())
				{
					AnimatedModule animatedModule = (AnimatedModule) animatedModuleEntity;
					binFile.writeShort (animatedModule.getChildrenCount());
					
					for (EntityObject moduleSequenceEntity : animatedModule.getChildren ())
					{
						ModuleSequence moduleSequence = (ModuleSequence) moduleSequenceEntity;
						writeModuleSequence (moduleSequence, binFile);
					}
				}
			}
		}
		
		// tile palette group
		{
			Sprite2dFile.TilePaletteSet tilePaletteSet = sprite2dFile.getTilePaletteSet();
			binFile.writeShort (tilePaletteSet.getChildrenCount());
			
			for (EntityObject entity : tilePaletteSet.getChildren())
			{
				Tiled2dPalette tilePalette = (Tiled2dPalette) entity;
				writeTiled2dGrid (tilePalette, binFile);
				
				for (EntityObject tiled2dModuleEntity : tilePalette.getChildren())
				{
					Tiled2dModule tiled2dModule = (Tiled2dModule) tiled2dModuleEntity;
					
					Module module = tiled2dModule.getModule();
					
					writeModuleRefWithFlipAndPaletteID (
							module, 
							tiled2dModule.isFlipX(), 
							tiled2dModule.isFlipY(), 
							tiled2dModule.getPaletteID(), 
							binFile
							);
				}
			}
		}
		
		// tile background group
		{
			Sprite2dFile.TiledBackgroundSet tiledBackgroundSet = sprite2dFile.getTiledBackgroundSet();
			binFile.writeShort (tiledBackgroundSet.getChildrenCount());
			
			for (EntityObject entity : tiledBackgroundSet.getChildren())
			{
				Tiled2dBackground tiled2dBackground = (Tiled2dBackground) entity;
				
				writeTiled2dGrid (tiled2dBackground, binFile);
				
				binFile.writeShort (tiled2dBackground.getChildrenCount());
				
				for (EntityObject tiled2dLayerEntity : tiled2dBackground.getChildren())
				{
					Tiled2dLayer tiled2dLayer = (Tiled2dLayer) tiled2dLayerEntity;
					writeTiled2dGrid (tiled2dLayer, binFile);
					
					for (int row = 0; row < tiled2dLayer.getCellRows(); ++ row)
					{
						for (int col = 0; col < tiled2dLayer.getCellCols(); ++ col)
						{
							Tiled2dLayerCell tile2dLayerCell = tiled2dLayer.getLayerCellByPos(row, col);

							Module module = tile2dLayerCell.getModule();
							
							writeModuleRefWithFlipAndPaletteID (
									module, 
									tile2dLayerCell.isFlipX(), 
									tile2dLayerCell.isFlipY(), 
									tile2dLayerCell.getPaletteID(), 
									binFile
									);
						}
					}
				}
			}
		}
	}
	
	public static int getModuleGroupID (ModuleGroup moduleGroup)
	{
		EntityObject groupSet = moduleGroup.getParent();
		int setID;
		if (groupSet instanceof Sprite2dFile.TilePaletteSet)
			setID = 3;
		else if (groupSet instanceof Sprite2dFile.AnimatedModuleGroupSet)
			setID = 2;
		else if (groupSet instanceof Sprite2dFile.CompositedModuleGroupSet)
			setID = 1;
		else
			setID = 0;
		
		int groupID = (setID << 12) | (moduleGroup.getIndex() & 0xFFF );
		
		return groupID;
	}
	
	boolean writeModuleRef (Module refedModule, RandomAccessFile binFile) throws Exception
	{
		//logger.assertLog(refedModule != null, "refedModule == null!");
		
		Module module = refedModule;		

		if (module == null)
		{
			binFile.writeShort (-1);
			
			return false;
		}
		else
		{
			ModuleGroup moduleGroup = module.getGroup();
			binFile.writeShort ( getModuleGroupID (moduleGroup) );
			binFile.writeShort (module.getIndex());
			
			return true;
		}
	}
	
	boolean writeModuleRefWithFlipAndPaletteID (Module refedModule, boolean flipX, boolean flipY, int paletteID, RandomAccessFile binFile) throws Exception
	{
		if (writeModuleRef (refedModule, binFile))
		{
			writeFlipAndPalette (flipX, flipY, paletteID, binFile);

			return true;
		}
		
		return false;
	}
	
	boolean writeModulePart (ModulePart modulePart, RandomAccessFile binFile) throws Exception
	{
		Module module = modulePart.getModule();
		
		if (writeModuleRefWithFlipAndPaletteID (
				modulePart.getModule(), 
				modulePart.isFlipX(), 
				modulePart.isFlipY(), 
				modulePart.getPaletteID(), 
				binFile) )
		{			
			binFile.writeShort (modulePart.getOffsetX());
			binFile.writeShort (modulePart.getOffsetY());
			
			return true;
		}
		
		return false;
	}
	
	boolean writeModuleSequence (ModuleSequence moduleSequence, RandomAccessFile binFile) throws Exception
	{
		if (writeModulePart (moduleSequence, binFile))
		{
			binFile.writeShort (moduleSequence.getDuration());
			
			return true;
		}
		
		return false;
	}
	
	void writeFlipAndPalette (boolean flipX, boolean flipY, int palId, RandomAccessFile binFile) throws Exception
	{
		int flags = 0;
		flags |= flipX ? 0x8000 : 0;
		flags |= flipY ? 0x4000 : 0;
		flags |= palId & 0xFF;
		binFile.writeShort (flags);
	}
	
	void writeTiled2dGrid (Tiled2dGrid grid, RandomAccessFile binFile) throws Exception
	{
		binFile.writeShort (grid.getCellRows());
		binFile.writeShort (grid.getCellCols());
		binFile.writeShort (grid.getCellWidth());
		binFile.writeShort (grid.getCellHeight());
	}
}
